// @import
import MsgCodec from "./MsgCodec";
import MsgRecognizer from "./MsgRecognizer";
import OutParam from "../util/OutParam";
// @import
import mod_moment = require("moment");

/**
 * 消息总线
 */
export default class MsgBus {
    /**
     * 单例对象
     */
    static readonly _oInstance: MsgBus = new MsgBus();

    /**
     * 服务器地址
     */
    _strServerAddr: string = "127.0.0.1:10240";

    /**
     * 使用加密方式
     */
    _xUseSSL: boolean = true;

    /**
     * WebSocket
     */
    _oWebSocket: WebSocket = null;

    /**
     * 连接尝试次数
     */
    _nConnTryCount: number = 0;

    /**
     * 消息队列
     */
    _oMsgQ: Array<{ _nMsgCode: number, _oMsgBody: any, }> = [];

    /**
     * 编解码器
     */
    readonly _msgCodec: MsgCodec = new MsgCodec();

    /**
     * 类默认构造器
     */
    private constructor() {
        if (null != MsgBus._oInstance) {
            throw new Error("该类为单例类");
        }
    }

    /**
     * 获取单例对象
     * 
     * @return 单例对象
     */
    static getInstance(): MsgBus {
        return this._oInstance;
    }

    /**
     * 设置服务器地址
     * 
     * @param strVal 字符串值
     * @returns this 指针
     */
    putServerAddr(strVal: string): MsgBus {
        this._strServerAddr = strVal;
        return this;
    }

    /**
     * 设置使用 SSL
     * 
     * @param xVal 布尔值
     * @returns this 指针
     */
    putUseSSL(xVal: boolean): MsgBus {
        this._xUseSSL = xVal;
        return this;
    }

    /**
     * 获取连接尝试次数
     */
    getConnTryCount(): number {
        return this._nConnTryCount;
    }

    /**
     * 是否已经准备好
     * 
     * @return true = 已经准备好, false = 未准备好
     */
    isReady(): boolean {
        return null != this._oWebSocket;
    }

    /**
     * 尝试初始化
     */
    tryInit(): { onOpen?: () => void, onError?: () => void, } {
        if (null != this._oWebSocket) {
            return null;
        }

        let oFuture = {
            onOpen: null, onError: null,
        };

        ++this._nConnTryCount;

        // 服务器地址
        let strServerAddr: string = this._xUseSSL 
            ? `wss://${this._strServerAddr}/herostory` : `ws://${this._strServerAddr}/herostory`;

        let oTempWS = new WebSocket(strServerAddr);
        oTempWS.binaryType = "arraybuffer";

        const SELF = this;

        let strCurrTime: string = mod_moment().format("YYYY/MM/DD HH:mm:ss.SSS");
        console.log(`准备连接服务器, currTime = ${strCurrTime}, serverAddr = ${strServerAddr}`);

        // open
        oTempWS.onopen = () => {
            let strCurrTime: string = mod_moment().format("YYYY/MM/DD HH:mm:ss.SSS");
            console.log(`已经连接到服务器, currTime = ${strCurrTime}, serverAddr = ${strServerAddr}`);

            SELF._oWebSocket = oTempWS;
            SELF._nConnTryCount = 0;

            if ("onOpen" in oFuture &&
                "function" == typeof (oFuture["onOpen"])) {
                oFuture["onOpen"]();
            }
        };

        // error
        oTempWS.onerror = () => {
            let strCurrTime: string = mod_moment().format("YYYY/MM/DD HH:mm:ss.SSS");
            console.error(`连接服务器失败, currTime = ${strCurrTime}, serverAddr = ${strServerAddr}`);

            if (null != oTempWS) {
                oTempWS.close();
            }

            oTempWS = null;
            SELF._oWebSocket = null;

            if ("onError" in oFuture &&
                "function" == typeof (oFuture["onError"])) {
                oFuture["onError"]();
            }
        };

        // close
        oTempWS.onclose = () => {
            let strCurrTime: string = mod_moment().format("YYYY/MM/DD HH:mm:ss.SSS");
            console.warn(`服务器连接已关闭, currTime = ${strCurrTime}`);

            oTempWS = null;
            SELF._oWebSocket = null;
        }

        // message
        oTempWS.onmessage = (oEvent: MessageEvent) => {
            // ( 输出参数 ) 消息代号和名称
            let out_oMsgCode = new OutParam<number>();
            let out_oMsgName = new OutParam<string>();
            // 反序列化为消息体
            let oMsgBody = this._msgCodec.decode(new Uint8Array(oEvent.data), out_oMsgCode, out_oMsgName);

            if ("object" != typeof (oMsgBody)) {
                return;
            }

            console.log(`从服务器端返回消息, msgCode = ${out_oMsgCode.get()}, msgName = ${out_oMsgName.get()}`);

            // 将消息加入队列
            SELF._oMsgQ.push({
                _nMsgCode: out_oMsgCode.get(),
                _oMsgBody: oMsgBody,
            });
        }

        return oFuture;
    }

    /**
     * 发送消息
     * 
     * @param nMsgCode 消息代号
     * @param oMsgBody 消息体
     */
    sendMsg(nMsgCode: number, oMsgBody: any): void {
        if (nMsgCode < 0 || 
            null == oMsgBody) {
            console.error("发送消息失败! 消息代号或消息体错误");
            return;
        }

        if (null == this._oWebSocket) {
            console.error("发送消息失败! WebSocket 尚未初始化");
            return;
        }

        // 序列化为字节数组
        let oUint8Array = this._msgCodec.encode(nMsgCode, oMsgBody);

        if (null == oUint8Array ||
            oUint8Array.byteLength <= 0) {
            console.error("发送消息失败! 消息编码错误");
            return;
        }

        // 获取消息类名称
        let out_oMsgClazzName = new OutParam<string>();
        MsgRecognizer.getMsgClazzByMsgCode(nMsgCode, out_oMsgClazzName);

        console.log(`发送消息, msgCode = ${nMsgCode}, msgClazz = ${out_oMsgClazzName.get()}`);

        // 发送字节数组
        this._oWebSocket.send(oUint8Array);
    }

    /**
     * 获取消息队列长度
     * 
     * @return 消息队列长度
     */
    getMsgQLen(): number {
        return this._oMsgQ.length;
    }

    /**
     * 取出一个消息
     * 
     * @return 消息包装对象
     */
    doMsgQShift(): { _nMsgCode: number, _oMsgBody: any, } {
        return this._oMsgQ.shift();
    }

    /**
     * 当收到消息, 需要被覆盖
     * 
     * @param nMsgCode 消息代号
     * @param oMsgBody 消息体
     */
    onMsgHandler(nMsgCode: number, oMsgBody: any): void {
        if (nMsgCode < 0 ||
            null == oMsgBody) {
            return;
        }
    }
}
